package io.noties.markwon.app.samples.tasklist;

import com.noties.markwon.annotation.Nullable;
import com.noties.markwon.utils.LogUtils;
import com.noties.markwon.wrapper.text.style.ClickableSpan;

import io.noties.markwon.AbstractMarkwonPlugin;
import io.noties.markwon.Markwon;
import io.noties.markwon.MarkwonVisitor;
import io.noties.markwon.SoftBreakAddsNewLinePlugin;
import io.noties.markwon.SpannableBuilder;
import io.noties.markwon.app.sample.Tags;
import io.noties.markwon.app.sample.ui.MarkwonTextViewSample;
import io.noties.markwon.ext.tasklist.TaskListItem;
import io.noties.markwon.ext.tasklist.TaskListPlugin;
import io.noties.markwon.ext.tasklist.TaskListProps;
import io.noties.markwon.ext.tasklist.TaskListSpan;
import io.noties.markwon.sample.annotations.MarkwonArtifact;
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
import ohos.agp.components.Component;

import org.commonmark.node.AbstractVisitor;
import org.commonmark.node.Block;
import org.commonmark.node.HardLineBreak;
import org.commonmark.node.Node;
import org.commonmark.node.Paragraph;
import org.commonmark.node.SoftLineBreak;
import org.commonmark.node.Text;
import org.jetbrains.annotations.NotNull;

/**
 * @author: yangrui
 * @function: TaskListMutateNestedSample
 * @date: 2021/3/23
 */
@MarkwonSampleInfo(
  id = "20211228120444",
  title = "Task list mutate nested",
  description = "Task list mutation with nested items",
  artifacts = {MarkwonArtifact.EXT_TASKLIST},
  tags = {Tags.plugin}
)
public class TaskListMutateNestedSample extends MarkwonTextViewSample {
  private static final String TAG = "TaskListMutateNestedSample";

  @Override
  public void render() {
    final String md = "# Task list\n" +
      "- [ ] not done\n" +
      "- [X] done\n" +
      "  - [ ] nested not done \n" +
      "    and text and textand text and text\n" +
      "  - [X] nested done".trim();

    final Markwon markwon = Markwon.builder(context)
      .usePlugin(TaskListPlugin.create(context))
      .usePlugin(SoftBreakAddsNewLinePlugin.create())
      .usePlugin(new AbstractMarkwonPlugin() {
        @Override
        public void configureVisitor(MarkwonVisitor.Builder builder) {
          builder.on(TaskListItem.class, (MarkwonVisitor.NodeVisitor<TaskListItem>) (visitor, node) -> {
            int length = visitor.length();
            visitor.visitChildren(node);

            TaskListProps.DONE.set(visitor.renderProps(), node.isDone());

            Object spans = visitor.configuration()
              .spansFactory()
              .get(TaskListItem.class)
              .getSpans(visitor.configuration(), visitor.renderProps());

            if (spans != null) {
              TaskListSpan taskListSpan = null;
              if (spans.getClass().isArray()) {
                Object span = ((Object[]) spans)[0];
                if (span instanceof TaskListSpan) {
                  spans = span;
                }
              } else {
                spans = (TaskListSpan) spans;
              }

              LogUtils.i(TAG, "#### " + visitor.builder().subSequence(length, length + 3));
              int content = TaskListContextVisitor.contentLength(node);
              LogUtils.i(TAG, "#### content:" + content + ", " + visitor.builder().subSequence(length, length + content));

              if (content > 0 && taskListSpan != null) {
                // maybe additionally identify this task list (for persistence)
                visitor.builder().setSpan(new ToggleTaskListSpan(
                  taskListSpan,
                  (String) visitor.builder().subSequence(length, length + content)
                ), length, length + content);
              }

              SpannableBuilder.setSpans(
                visitor.builder(),
                spans,
                length,
                visitor.length()
              );

              if (visitor.hasNext(node)) {
                visitor.ensureNewLine();
              }
            }
          });
        }
      })
      .build();

    markwon.setMarkdown(textView, md);
  }

  static class TaskListContextVisitor extends AbstractVisitor {
    static int contentLength(Node node) {
      final TaskListContextVisitor visitor = new TaskListContextVisitor();
      visitor.visitChildren(node);
      return contentLength;
    }

    private static int contentLength = 0;

    @Override
    public void visit(@NotNull Text text) {
      super.visit(text);
      contentLength += text.getLiteral().length();
    }

    @Override
    public void visit(@Nullable SoftLineBreak softLineBreak) {
      super.visit(softLineBreak);
      contentLength += 1;
    }

    @Override
    public void visit(@Nullable HardLineBreak hardLineBreak) {
      super.visit(hardLineBreak);
      contentLength += 1;
    }

    @Override
    protected void visitChildren(@NotNull Node parent) {
      Node node = parent.getFirstChild();
      while (node != null) {
        // A subclass of this visitor might modify the node, resulting in getNext returning a different node or no
        // node after visiting it. So get the next node before visiting.
        final Node next = node.getNext();
        if (node instanceof Block && (!(node instanceof Paragraph))) {
          break;
        }
        node.accept(this);
        node = next;
      }
    }
  }

  public static final class ToggleTaskListSpan extends ClickableSpan {
    @NotNull
    private final TaskListSpan span;
    @NotNull
    private final String content;

    @Override
    public void onClick(@NotNull Component widget) {
      this.span.setDone(!this.span.isDone());
      widget.invalidate();
      LogUtils.i(TAG, "task-list click, isDone: " + this.span.isDone() + ", content: '" + this.content + '\'');
    }

    @NotNull
    public final TaskListSpan getSpan() {
      return this.span;
    }

    @NotNull
    public final String getContent() {
      return this.content;
    }

    ToggleTaskListSpan(@NotNull TaskListSpan span, @NotNull String content) {
      super();
      this.span = span;
      this.content = content;
    }
  }
}
